#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <elf.h>

/**
 * @brief 从指定的文件偏移位置读取内容到缓冲区
 * 
 * 该函数用于从文件描述符指定的文件中读取给定大小的数据到提供的缓冲区中。
 * 
 * @param fd 文件描述符
 * @param offset 读取数据的偏移量
 * @param buf 存储读取数据的缓冲区指针
 * @param size 要读取的数据大小
 */
void read_at_offset(int fd, off_t offset, void *buf, size_t size) {
    if (lseek(fd, offset, SEEK_SET) < 0) {
        perror("lseek failed");
        exit(EXIT_FAILURE);
    }
    if (read(fd, buf, size) != size) {
        perror("read failed");
        exit(EXIT_FAILURE);
    }
}

/**
 * @brief 打印ELF文件中每个节的名字和大小
 * 
 * 该函数读取ELF节头表和节名称字符串表，并打印每个节的名称和大小。
 * 
 * @param fd 文件描述符
 * @param ehdr 指向ELF头的指针
 */
void print_section_info(int fd, const Elf64_Ehdr *ehdr) {
    // 读取节头表
    Elf64_Shdr *section_headers = malloc(ehdr->e_shentsize * ehdr->e_shnum);
    if (!section_headers) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }
    read_at_offset(fd, ehdr->e_shoff, section_headers, ehdr->e_shentsize * ehdr->e_shnum);

    // 读取节名称字符串表
    Elf64_Shdr *shstrtab_header = &section_headers[ehdr->e_shstrndx];
    char *shstrtab = malloc(shstrtab_header->sh_size);
    if (!shstrtab) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }
    read_at_offset(fd, shstrtab_header->sh_offset, shstrtab, shstrtab_header->sh_size);

    printf("\nSection Headers:\n");
    for (int i = 0; i < ehdr->e_shnum; ++i) {
        const char *section_name = shstrtab + section_headers[i].sh_name;
        printf("  [%2d] %s: Size = %lu bytes\n", i, section_name, section_headers[i].sh_size);
    }

    free(shstrtab);
    free(section_headers);
}

/**
 * @brief 主函数
 * 
 * 该函数打开并读取指定的ELF文件，验证其有效性，然后打印文件中各个节的名称和大小。
 * 
 * @param argc 参数数量
 * @param argv 参数值数组
 * @return int 程序执行状态码
 */
int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <elf-file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("Failed to open ELF file");
        return EXIT_FAILURE;
    }

    // 读取 ELF64 头
    Elf64_Ehdr ehdr;
    read_at_offset(fd, 0, &ehdr, sizeof(Elf64_Ehdr));

    // 检查 ELF 魔数
    if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
        fprintf(stderr, "Not a valid ELF file\n");
        close(fd);
        return EXIT_FAILURE;
    }

    print_section_info(fd, &ehdr);

    close(fd);
    return EXIT_SUCCESS;
}
